go(goav) 中使用 ffmpeg 获取摄像头视频流,并转换成图片,发送给前端界面实时展示

您所在的位置:网站首页 rtmp 截图 go(goav) 中使用 ffmpeg 获取摄像头视频流,并转换成图片,发送给前端界面实时展示

go(goav) 中使用 ffmpeg 获取摄像头视频流,并转换成图片,发送给前端界面实时展示

2024-07-10 01:35:34| 来源: 网络整理| 查看: 265

本文的主角: ffmpeg goav。 之前写过一篇文章,实时展示摄像头内容 中有提到过一种实时展示摄像头内容的方式:集成ffmpeg相关的代码,并转换成图片传给web界面进行相应的展示,现补充下具体的实现。

ffmpeg简介

ffmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案(百度百科)。

goav

一个开源库,能够方便的在 go 中使用 C 的相应方法。要使用 goav,还是得先熟悉 ffmpeg 的基本使用方式,这篇就不详述了,可以看大神雷霄骅的相关文章进行学习(真的赞)。 该仓库主要是通过 cgo 对 c 中的相关方法进行绑定,前人栽树后人乘凉,这里就直接拿来用了。但是该库集成的东西并不全面,若感觉不可靠,可自行实现。

实际效果

在这里插入图片描述

直接上代码

这里主要使用 go 的 gin 框架作为 http 服务器,通过 websocket 实时地推送图片给 web 界面进行展示。

go package api import ( "bytes" "encoding/base64" "fmt" "github.com/gin-gonic/gin" "github.com/giorgisio/goav/avcodec" "github.com/giorgisio/goav/avdevice" "github.com/giorgisio/goav/avformat" "github.com/giorgisio/goav/avutil" "github.com/giorgisio/goav/swscale" "github.com/gorilla/websocket" "image" "image/color" "image/jpeg" "net/http" "unsafe" ) type wsParam struct { Name string } // 用于测试实时读取摄像头的内容,并发送 base64 给界面 func ConnectWs(c *gin.Context) { var param wsParam if err := c.Bind(¶m); err != nil { c.String(http.StatusBadRequest, err.Error()) return } var wsUpgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true }, // disable origin check } conn, err := wsUpgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { c.String(http.StatusInternalServerError, err.Error()) return } defer conn.Close() go tSendPic(conn) _, _ , err = conn.ReadMessage() if err != nil { fmt.Println(err) } } func tSendPic(conn *websocket.Conn) { avdevice.AvdeviceRegisterAll() avcodec.AvcodecRegisterAll() pFormatCtx := avformat.AvformatAllocContext() defer pFormatCtx.AvformatCloseInput() inputFmt := avformat.AvFindInputFormat("dshow") // 注意这里的第二个参数,需要改成自己的摄像头名称 if ret := avformat.AvformatOpenInput(&pFormatCtx, "video=USB2.0 PC CAMERA", inputFmt, nil); ret != 0 { fmt.Println("Unable to open video USB2.0 PC CAMERA") return } if pFormatCtx.AvformatFindStreamInfo(nil) streams := pFormatCtx.Streams() if streams[i].Codec().GetCodecType() == avformat.AVMEDIA_TYPE_VIDEO { videoindex = i break } } if videoindex == -1 { fmt.Printf("Couldn't find a video stream.\n") return } pCodecCtxOrig := pFormatCtx.Streams()[videoindex].Codec() defer (*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig)).AvcodecClose() pCodec := avcodec.AvcodecFindDecoder(avcodec.CodecId(pCodecCtxOrig.GetCodecId())) if pCodec == nil { fmt.Println("没有找到解码器") return } pCodecCtx := pCodec.AvcodecAllocContext3() defer pCodecCtx.AvcodecClose() if pCodecCtx.AvcodecCopyContext((*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig))) != 0 { fmt.Println("Couldn't copy codec context") return } // Open codec if pCodecCtx.AvcodecOpen2(pCodec, nil) fmt.Println("Unable to allocate Frame") return } defer avutil.AvFrameFree(pFrame) pFrameRGB := avutil.AvFrameAlloc() if pFrameRGB == nil { fmt.Println("Unable to allocate RGB Frame") return } defer avutil.AvFrameFree(pFrameRGB) width := pCodecCtx.Width() height := pCodecCtx.Height() img_convert_ctx := swscale.SwsGetcontext( width, height, (swscale.PixelFormat)(pCodecCtx.PixFmt()), width, height, avcodec.AV_PIX_FMT_RGB24, avcodec.SWS_BILINEAR, nil, nil, nil, ) numBytes := uintptr(avcodec.AvpictureGetSize(avcodec.AV_PIX_FMT_RGB24, width, height)) buffer := avutil.AvMalloc(numBytes) defer avutil.AvFree(buffer) avp := (*avcodec.Picture)(unsafe.Pointer(pFrameRGB)) avp.AvpictureFill((*uint8)(buffer), avcodec.AV_PIX_FMT_RGB24, width, height) packet := avcodec.AvPacketAlloc() defer packet.AvFreePacket() for pFormatCtx.AvReadFrame(packet) >= 0 { if packet.StreamIndex() != videoindex { continue } var got_picture int if ret := pCodecCtx.AvcodecDecodeVideo2((*avcodec.Frame)(unsafe.Pointer(pFrame)), &got_picture, packet); ret swscale.SwsScale2(img_convert_ctx, avutil.Data(pFrame), avutil.Linesize(pFrame), 0, height, avutil.Data(pFrameRGB), avutil.Linesize(pFrameRGB)) // 直接存储 // 转成图片并传给界面 if buf, err := getJpegStreams(pFrameRGB, width, height); err == nil { if er := conn.WriteMessage(websocket.TextMessage, buf); er != nil { fmt.Println("write err: ", er) return } } } else { fmt.Println("got_picture err: ", got_picture) } } fmt.Println("exit video!!!") } func getJpegStreams(frame *avutil.Frame, width, height int) ([]byte, error) { img := image.NewRGBA(image.Rect(0, 0, width, height)) for y := 0; y var pixel = make([]byte, 3) for i := 0; i pixel[0], pixel[1], pixel[2], 0xff}) } } var b []byte buffer := bytes.NewBuffer(b) err := jpeg.Encode(buffer, img, nil) if err != nil { fmt.Println("jpeg.Encode err: ", err) return nil, err } dst := make([]byte, base64.StdEncoding.EncodedLen(buffer.Len())) base64.StdEncoding.Encode(dst, buffer.Bytes()) return dst, nil } html ws获取图片数据展示 测试服务器返回值 打开连接 关闭连接 var img = document.getElementById("img"); var ws; function closeWs() { if (ws != null) { console.log("now close ws"); ws.close(); ws = null; } } function openWs() { if (ws != null) { alert("已连接"); return } ws = new WebSocket("ws://127.0.0.1:4780/ws?name=grassto"); ws.onopen = function() { console.log("open"); } ws.onmessage = function(e){ //当客户端收到服务端发来的消息时,触发onmessage事件,参数e.data包含server传递过来的数据 // console.log(e); img.src = "data:image/jpg;base64," + e.data; } ws.onclose = function(e){ //当客户端收到服务端发送的关闭连接请求时,触发onclose事件 console.log("close"); } ws.onerror = function(e){ //如果出现连接、处理、接收、发送数据失败的时候触发onerror事件 console.log(e); } }

完整的代码可见gitee(平时写的一些测试都在这个仓库)。该示例的代码在 ginServer 目录下。 在这里插入图片描述

总结

其实难点主要还是在于 ffmpeg 的使用,由于时间精力有限,并未进行深入研究,对音视频感兴趣的小伙伴可以去看雷神(雷霄骅)的博客。 感慨一下:知道雷神这个人,是在搜索 ffmpeg 的相关用法的时候出现的博客。翻看其博客内容,真是大为惊叹,文章通俗易懂,排版格式也看的很舒服,默默的给了个赞加关注。但是翻到最后评论的时候,才知道天妒英才,雷神已经走了,惋惜。 不求成为栽树人,但愿变成养树人。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭